"""
裁剪库窗口
"""

import tkinter as tk
from tkinter import filedialog, messagebox, ttk
from PIL import Image, ImageTk
import numpy as np
from pathlib import Path
from typing import Optional, Callable

from ..models import ClipManager, ClipItem
from ..utils import get_logger, FileUtils, ClipboardUtils
from ..services.rembg_runner import RembgRunner, RembgError


class ClipLibraryWindow:
    """裁剪库窗口类"""
    
    def __init__(
        self,
        parent: tk.Tk,
        clip_manager: ClipManager,
        on_close: Optional[Callable] = None
    ):
        """
        初始化裁剪库窗口
        
        Args:
            parent: 父窗口
            clip_manager: 裁剪管理器
            on_close: 关闭回调
        """
        self.parent = parent
        self.clip_manager = clip_manager
        self.on_close_callback = on_close
        self.logger = get_logger(__name__)
        
        # 创建窗口
        self.window = tk.Toplevel(parent)
        self.window.title("裁剪库")
        self.window.geometry("1366x768")
        self.window.transient(parent)
        
        # 绑定关闭事件
        self.window.protocol("WM_DELETE_WINDOW", self.close)
        
        # 选中的裁剪项
        self.selected_clip_id = None
        
        # 当前选中的分类
        self.current_category = "全部"
        
        # 缩略图引用（防止被垃圾回收）
        self.thumbnail_photos = {}
        self.rembg_runner: Optional[RembgRunner] = None
        
        # 创建UI
        self.setup_ui()
        
        # 刷新显示
        self.refresh()
        
        self.logger.info("裁剪库窗口已打开")
    
    def setup_ui(self):
        """设置用户界面"""
        # 顶部工具栏
        toolbar = ttk.Frame(self.window, padding=10)
        toolbar.pack(side=tk.TOP, fill=tk.X, padx=5, pady=5)
        
        # 第一行：分类管理
        category_bar = ttk.Frame(toolbar)
        category_bar.pack(side=tk.TOP, fill=tk.X, padx=5, pady=5)
        
        ttk.Label(category_bar, text="分类:", font=("Arial", 10, "bold")).pack(side=tk.LEFT, padx=(0, 10))
        
        # 分类按钮容器
        self.category_buttons_frame = ttk.Frame(category_bar)
        self.category_buttons_frame.pack(side=tk.LEFT, fill=tk.X, expand=True)
        
        # 分类管理按钮
        ttk.Button(
            category_bar, text="➕ 添加分类", command=self.add_category,
            bootstyle="success", width=12
        ).pack(side=tk.RIGHT, padx=2)
        
        ttk.Button(
            category_bar, text="✎ 重命名", command=self.rename_selected,
            bootstyle="primary", width=10
        ).pack(side=tk.RIGHT, padx=2)
        
        ttk.Button(
            category_bar, text="📁 修改分类", command=self.change_category,
            bootstyle="warning", width=12
        ).pack(side=tk.RIGHT, padx=2)
        
        # 分隔线
        ttk.Separator(toolbar, orient=tk.HORIZONTAL).pack(fill=tk.X, pady=5)
        
        # 第二行：操作按钮
        
        operation_bar = ttk.Frame(toolbar)
        operation_bar.pack(side=tk.TOP, fill=tk.X, padx=5, pady=(0, 5))
        
        ttk.Button(
            operation_bar, text="刷新", command=self.refresh,
            bootstyle="primary", width=10
        ).pack(side=tk.LEFT, padx=3)
        
        ttk.Button(
            operation_bar, text="删除选中", command=self.delete_selected,
            bootstyle="danger", width=10
        ).pack(side=tk.LEFT, padx=3)
        
        ttk.Button(
            operation_bar, text="导出选中", command=self.export_selected,
            bootstyle="success", width=10
        ).pack(side=tk.LEFT, padx=3)
        
        ttk.Button(
            operation_bar, text="复制选中", command=self.copy_selected,
            bootstyle="info", width=10
        ).pack(side=tk.LEFT, padx=3)
        
        ttk.Button(
            operation_bar, text="批量导出", command=self.batch_export,
            bootstyle="warning", width=10
        ).pack(side=tk.LEFT, padx=3)
        
        ttk.Button(
            operation_bar, text="清空库", command=self.clear_all,
            bootstyle="secondary", width=10
        ).pack(side=tk.LEFT, padx=3)
        
        # 统计信息
        self.stats_label = ttk.Label(operation_bar, text="", font=("Arial", 10, "bold"))
        self.stats_label.pack(side=tk.RIGHT, padx=10)
        
        # 主内容区域
        main_frame = ttk.Frame(self.window)
        main_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 创建滚动区域
        canvas_frame = ttk.Frame(main_frame)
        canvas_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        # 添加滚动条
        scrollbar = ttk.Scrollbar(canvas_frame, orient=tk.VERTICAL)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        self.canvas = tk.Canvas(
            canvas_frame,
            bg="#f0f0f0",
            yscrollcommand=scrollbar.set,
            highlightthickness=0
        )
        self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.config(command=self.canvas.yview)
        
        # 创建内部框架用于放置缩略图
        self.clips_frame = ttk.Frame(self.canvas)
        self.canvas_window = self.canvas.create_window(
            (0, 0),
            window=self.clips_frame,
            anchor=tk.NW
        )
        
        # 绑定事件
        self.clips_frame.bind("<Configure>", self._on_frame_configure)
        self.canvas.bind("<Configure>", self._on_canvas_configure)
        
        # 右侧预览面板
        preview_frame = ttk.LabelFrame(main_frame, text="预览", padding=5)
        preview_frame.pack(side=tk.RIGHT, fill=tk.BOTH, padx=(5, 0))
        
        self.preview_canvas = tk.Canvas(
            preview_frame,
            width=300,
            height=300,
            bg="#2b2b2b",
            highlightthickness=0
        )
        self.preview_canvas.pack(padx=5, pady=5)
        
        # 预览信息
        info_frame = ttk.Frame(preview_frame)
        info_frame.pack(fill=tk.X, padx=5, pady=5)
        
        self.info_text = tk.Text(
            info_frame,
            height=8,
            width=30,
            font=("Arial", 9),
            state='disabled'
        )
        self.info_text.pack(fill=tk.BOTH, expand=True)
        
        # 底部状态栏
        self.status_bar = ttk.Label(
            self.window,
            text="就绪",
            relief=tk.SUNKEN,
            anchor=tk.W,
            padding=5
        )
        self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
    
    def refresh(self):
        """刷新裁剪库显示"""
        # 刷新分类按钮
        self._refresh_category_buttons()
        
        # 清空现有内容
        for widget in self.clips_frame.winfo_children():
            widget.destroy()
        
        self.thumbnail_photos.clear()
        
        # 获取当前分类的裁剪项
        clips = self.clip_manager.get_clips_by_category(self.current_category)
        
        if not clips:
            # 显示空状态
            ttk.Label(
                self.clips_frame,
                text="裁剪库为空\n\n请在主窗口中选择对象后\n点击「添加到裁剪库」按钮",
                font=("Arial", 12),
                foreground="#999"
            ).pack(pady=50)
        else:
            # 显示缩略图网格
            self._display_clips_grid(clips)
        
        # 更新统计信息
        total = self.clip_manager.get_clip_count()
        if self.current_category == "全部":
            self.stats_label.config(text=f"共 {total} 项")
            self.status_bar.config(text=f"已加载 {total} 个裁剪项")
        else:
            self.stats_label.config(text=f"当前分类 {len(clips)} 项 / 总计 {total} 项")
            self.status_bar.config(text=f"分类「{self.current_category}」: {len(clips)} 个裁剪项")
    
    def _display_clips_grid(self, clips):
        """显示裁剪项网格"""
        # 每行显示的缩略图数量
        cols = 4
        padding = 10
        
        for i, clip in enumerate(clips):
            row = i // cols
            col = i % cols
            
            # 创建裁剪项卡片
            card = self._create_clip_card(clip)
            card.grid(row=row, column=col, padx=padding, pady=padding)
    
    def _create_clip_card(self, clip: ClipItem) -> ttk.Frame:
        """
        创建裁剪项卡片
        
        Args:
            clip: 裁剪项
        
        Returns:
            ttk.Frame: 卡片框架
        """
        # 卡片框架
        card = ttk.Frame(
            self.clips_frame,
            relief=tk.RAISED,
            borderwidth=2,
            cursor="hand2",
            padding=5
        )
        
        # 缩略图
        if clip.thumbnail is not None:
            pil_image = Image.fromarray(clip.thumbnail)
            photo = ImageTk.PhotoImage(pil_image)
            
            thumbnail_label = ttk.Label(card, image=photo)
            thumbnail_label.pack(padx=5, pady=5)
            
            # 保存引用
            self.thumbnail_photos[clip.id] = photo
        
        # 名称
        name_label = ttk.Label(
            card,
            text=clip.name,
            font=("Arial", 9)
        )
        name_label.pack(padx=5, pady=(0, 5))
        
        # 绑定点击事件
        for widget in [card, thumbnail_label, name_label]:
            widget.bind("<Button-1>", lambda e, c=clip: self.select_clip(c))
            widget.bind("<Button-3>", lambda e, c=clip: self._show_clip_context_menu(e, c))
        
        return card
    
    def select_clip(self, clip: ClipItem):
        """
        选择裁剪项
        
        Args:
            clip: 裁剪项
        """
        self.selected_clip_id = clip.id
        self.logger.debug(f"选中裁剪项: {clip.name} (ID: {clip.id})")
        
        # 更新预览
        self._update_preview(clip)
        
        # 高亮选中的卡片
        self._highlight_selected()
    
    def _update_preview(self, clip: ClipItem):
        """
        更新预览
        
        Args:
            clip: 裁剪项
        """
        # 清空画布
        self.preview_canvas.delete("all")
        
        # 显示预览图
        if clip.masked_image is not None:
            # 获取画布尺寸
            canvas_width = 300
            canvas_height = 300
            
            # 调整图片大小
            h, w = clip.masked_image.shape[:2]
            scale = min(canvas_width / w, canvas_height / h) * 0.9
            
            new_w = int(w * scale)
            new_h = int(h * scale)
            
            import cv2
            resized = cv2.resize(clip.masked_image, (new_w, new_h))
            
            # 创建带背景的预览图
            preview = np.ones((canvas_height, canvas_width, 3), dtype=np.uint8) * 43  # 深灰背景
            
            offset_x = (canvas_width - new_w) // 2
            offset_y = (canvas_height - new_h) // 2
            
            # Alpha混合
            if resized.shape[2] == 4:
                alpha = resized[:, :, 3:4] / 255.0
                rgb = resized[:, :, :3]
                
                y1, y2 = offset_y, offset_y + new_h
                x1, x2 = offset_x, offset_x + new_w
                preview[y1:y2, x1:x2] = (
                    rgb * alpha + preview[y1:y2, x1:x2] * (1 - alpha)
                ).astype(np.uint8)
            else:
                preview[offset_y:offset_y+new_h, offset_x:offset_x+new_w] = resized[:, :, :3]
            
            # 转换为PhotoImage
            pil_preview = Image.fromarray(preview)
            photo = ImageTk.PhotoImage(pil_preview)
            
            self.preview_canvas.create_image(0, 0, anchor=tk.NW, image=photo)
            self.preview_canvas.image = photo
        
        # 更新信息文本
        self.info_text.config(state='normal')
        self.info_text.delete(1.0, tk.END)
        
        info = f"名称: {clip.name}\n"
        info += f"ID: {clip.id}\n"
        info += f"分类: {clip.category}\n"
        info += f"尺寸: {clip.masked_image.shape[1]}x{clip.masked_image.shape[0]}\n"
        if clip.score > 0:
            info += f"得分: {clip.score:.3f}\n"
        info += f"创建时间:\n{clip.created_time.strftime('%Y-%m-%d %H:%M:%S')}\n"
        
        self.info_text.insert(1.0, info)
        self.info_text.config(state='disabled')
    
    def _highlight_selected(self):
        """高亮选中的卡片"""
        # 重置所有卡片
        for widget in self.clips_frame.winfo_children():
            if isinstance(widget, ttk.Frame):
                widget.config(relief=tk.RAISED, borderwidth=2)
        
        # 高亮选中的卡片
        if self.selected_clip_id:
            clips = self.clip_manager.get_all_clips()
            for i, clip in enumerate(clips):
                if clip.id == self.selected_clip_id:
                    cards = [w for w in self.clips_frame.winfo_children() if isinstance(w, ttk.Frame)]
                    if i < len(cards):
                        cards[i].config(relief=tk.SOLID, borderwidth=3)
                    break
    
    def delete_selected(self):
        """删除选中的裁剪项"""
        if not self.selected_clip_id:
            messagebox.showwarning("警告", "请先选择要删除的裁剪项！", parent=self.window)
            return
        
        clip = self.clip_manager.get_clip(self.selected_clip_id)
        if not clip:
            return
        
        # 确认删除
        if not messagebox.askyesno(
            "确认删除",
            f"确定要删除「{clip.name}」吗？",
            parent=self.window
        ):
            return
        
        # 删除
        if self.clip_manager.remove_clip(self.selected_clip_id):
            self.selected_clip_id = None
            self.refresh()
            messagebox.showinfo("成功", "裁剪项已删除", parent=self.window)
    
    def export_selected(self):
        """导出选中的裁剪项"""
        if not self.selected_clip_id:
            messagebox.showwarning("警告", "请先选择要导出的裁剪项！", parent=self.window)
            return
        
        clip = self.clip_manager.get_clip(self.selected_clip_id)
        if not clip:
            return
        
        # 选择保存路径
        default_dir = FileUtils.get_desktop_path()
        default_name = f"{clip.name}.png"
        
        save_path = filedialog.asksaveasfilename(
            title="导出裁剪项",
            initialdir=default_dir,
            initialfile=default_name,
            defaultextension=".png",
            filetypes=[("PNG 图片", "*.png"), ("所有文件", "*.*")]
        )
        
        if not save_path:
            return
        
        # 保存
        if self.clip_manager.save_clip(self.selected_clip_id, save_path):
            messagebox.showinfo("成功", f"裁剪项已导出到:\n{save_path}", parent=self.window)
        else:
            messagebox.showerror("错误", "导出失败！", parent=self.window)
    
    def copy_selected(self):
        """复制选中的裁剪项到剪贴板"""
        if not self.selected_clip_id:
            messagebox.showwarning("警告", "请先选择要复制的裁剪项！", parent=self.window)
            return
        
        self.copy_clip(self.selected_clip_id)
    
    def copy_clip(self, clip_id: str):
        """
        复制指定裁剪项到剪贴板
        
        Args:
            clip_id: 裁剪项ID
        """
        clip = self.clip_manager.get_clip(clip_id)
        if not clip:
            messagebox.showerror("错误", "裁剪项不存在！", parent=self.window)
            return
        
        # 检查剪贴板支持
        if not ClipboardUtils.has_clipboard_support():
            messagebox.showerror("错误", "系统剪贴板不可用！", parent=self.window)
            return
        
        # 复制到剪贴板（使用PNG格式保留透明通道）
        if ClipboardUtils.copy_png_to_clipboard(clip.masked_image):
            self.status_bar.config(text=f"已复制「{clip.name}」到剪贴板（含透明通道）")
            messagebox.showinfo(
                "成功",
                f"图片已复制到剪贴板！\n\n"
                f"名称: {clip.name}\n"
                f"尺寸: {clip.masked_image.shape[1]}x{clip.masked_image.shape[0]}\n\n"
                f"现在可以直接粘贴到P图工具中",
                parent=self.window
            )
            self.logger.info(f"已复制裁剪项到剪贴板: {clip.name} (ID: {clip_id})")
        else:
            messagebox.showerror("错误", "复制到剪贴板失败！\n请查看日志了解详情。", parent=self.window)
    
    def replace_clip_file(self, clip: ClipItem):
        """
        通过外部文件替换裁剪项图片
        
        Args:
            clip: 需要替换的裁剪项
        """
        if clip is None:
            messagebox.showerror("错误", "未找到要替换的素材。", parent=self.window)
            return
        
        initial_dir = str(FileUtils.get_desktop_path())
        file_path = filedialog.askopenfilename(
            title="选择新的素材文件",
            initialdir=initial_dir,
            filetypes=[
                ("图片文件", "*.png;*.jpg;*.jpeg;*.bmp;*.tiff;*.webp"),
                ("PNG 图片", "*.png"),
                ("所有文件", "*.*")
            ]
        )
        
        if not file_path:
            self.status_bar.config(text="已取消替换素材操作")
            return
        
        if not FileUtils.is_image_file(file_path):
            messagebox.showerror("错误", "请选择图片文件进行替换。", parent=self.window)
            return
        
        try:
            with Image.open(file_path) as imported_image:
                rgba_image = imported_image.convert("RGBA")
                new_data = np.array(rgba_image)
        except Exception as exc:
            self.logger.error(f"加载替换文件失败: {file_path}", exc_info=True)
            messagebox.showerror("错误", f"读取图片失败：\n{exc}", parent=self.window)
            return
        
        if not self.clip_manager.update_clip_image(clip.id, new_data):
            messagebox.showerror("错误", "素材替换失败，请查看日志。", parent=self.window)
            return
        
        self.selected_clip_id = clip.id
        self.refresh()
        updated_clip = self.clip_manager.get_clip(clip.id)
        if updated_clip:
            self.select_clip(updated_clip)
        
        self.status_bar.config(text=f"已替换素材「{clip.name}」")
        messagebox.showinfo("成功", f"素材「{clip.name}」已更新完成。", parent=self.window)
    
    def remove_clip_background(self, clip: ClipItem):
        """
        调用 rembg 去除素材背景
        
        Args:
            clip: 目标裁剪项
        """
        if clip is None:
            messagebox.showerror("错误", "未找到要处理的素材。", parent=self.window)
            return
        
        if clip.masked_image is None:
            messagebox.showerror("错误", "当前素材没有可用的图片数据。", parent=self.window)
            return
        
        try:
            runner = self._ensure_rembg_runner()
        except RembgError as exc:
            messagebox.showerror("错误", f"无法执行背景去除：\n{exc}", parent=self.window)
            self.status_bar.config(text="背景去除失败")
            return
        
        self.status_bar.config(text=f"正在去除背景: {clip.name} …")
        self.window.update_idletasks()
        
        try:
            result = runner.remove_background(clip.masked_image)
        except RembgError as exc:
            self.logger.error("rembg 处理失败", exc_info=True)
            messagebox.showerror("错误", f"背景去除失败：\n{exc}", parent=self.window)
            self.status_bar.config(text="背景去除失败")
            return
        except Exception as exc:
            self.logger.error("调用 rembg 出现异常", exc_info=True)
            messagebox.showerror("错误", f"背景去除出现异常：\n{exc}", parent=self.window)
            self.status_bar.config(text="背景去除失败")
            return
        
        if not self.clip_manager.update_clip_image(clip.id, result):
            messagebox.showerror("错误", "更新素材失败，请查看日志。", parent=self.window)
            self.status_bar.config(text="背景去除失败")
            return
        
        self.selected_clip_id = clip.id
        self.refresh()
        updated_clip = self.clip_manager.get_clip(clip.id)
        if updated_clip:
            self.select_clip(updated_clip)
        
        self.status_bar.config(text=f"背景去除完成: {clip.name}")
        messagebox.showinfo("成功", "背景去除完成，素材已更新。", parent=self.window)
    
    def _ensure_rembg_runner(self) -> RembgRunner:
        """懒加载 rembg 运行器"""
        if self.rembg_runner is not None:
            return self.rembg_runner
        
        try:
            self.rembg_runner = RembgRunner()
        except RembgError as exc:
            raise RembgError(
                f"{exc}\n\n请先安装 rembg 库，例如：\npython -m pip install rembg"
            )
        
        return self.rembg_runner
    
    def batch_export(self):
        """批量导出所有裁剪项"""
        if self.clip_manager.get_clip_count() == 0:
            messagebox.showwarning("警告", "裁剪库为空！", parent=self.window)
            return
        
        # 选择输出目录
        output_dir = filedialog.askdirectory(
            title="选择输出目录",
            initialdir=FileUtils.get_desktop_path()
        )
        
        if not output_dir:
            return
        
        # 批量导出
        count = self.clip_manager.batch_export(output_dir, "clip")
        
        messagebox.showinfo(
            "完成",
            f"批量导出完成！\n\n成功: {count}/{self.clip_manager.get_clip_count()}\n输出目录: {output_dir}",
            parent=self.window
        )
    
    def clear_all(self):
        """清空裁剪库"""
        if self.clip_manager.get_clip_count() == 0:
            messagebox.showinfo("提示", "裁剪库已经是空的", parent=self.window)
            return
        
        # 确认清空
        if not messagebox.askyesno(
            "确认清空",
            f"确定要清空裁剪库吗？\n\n将删除所有 {self.clip_manager.get_clip_count()} 个裁剪项！",
            parent=self.window
        ):
            return
        
        # 清空
        self.clip_manager.clear()
        self.selected_clip_id = None
        self.refresh()
        
        messagebox.showinfo("成功", "裁剪库已清空", parent=self.window)
    
    def close(self):
        """关闭窗口"""
        self.logger.info("关闭裁剪库窗口")
        
        if self.on_close_callback:
            self.on_close_callback()
        
        self.window.destroy()
    
    def _on_frame_configure(self, event=None):
        """框架配置改变"""
        self.canvas.configure(scrollregion=self.canvas.bbox("all"))
    
    def _refresh_category_buttons(self):
        """刷新分类按钮"""
        # 清空现有按钮
        for widget in self.category_buttons_frame.winfo_children():
            widget.destroy()
        
        # 获取所有分类
        categories = self.clip_manager.get_categories()
        
        # 创建分类按钮
        for category in categories:
            # 统计该分类下的数量
            count = len(self.clip_manager.get_clips_by_category(category))
            
            # 按钮样式
            if category == self.current_category:
                bg_color = "#2196F3"
                fg_color = "white"
                relief = tk.SUNKEN
            else:
                bg_color = "#E0E0E0"
                fg_color = "black"
                relief = tk.RAISED
            
            # 创建按钮
            if category == self.current_category:
                bootstyle = "primary"
            else:
                bootstyle = "secondary-outline"
            
            btn = ttk.Button(
                self.category_buttons_frame,
                text=f"{category} ({count})",
                command=lambda c=category: self.switch_category(c),
                bootstyle=bootstyle,
                width=max(10, len(category) + 5)
            )
            btn.pack(side=tk.LEFT, padx=2)
            
            # 为非"全部"分类添加右键菜单（删除分类）
            if category != "全部":
                btn.bind("<Button-3>", lambda e, c=category: self._show_category_menu(e, c))
    
    def _show_category_menu(self, event, category):
        """显示分类右键菜单"""
        menu = tk.Menu(self.window, tearoff=0)
        menu.add_command(
            label=f"删除分类「{category}」",
            command=lambda: self.delete_category(category)
        )
        menu.post(event.x_root, event.y_root)
    
    def _show_clip_context_menu(self, event, clip: ClipItem):
        """
        显示裁剪项右键菜单
        
        Args:
            event: 鼠标事件
            clip: 裁剪项
        """
        # 先选中该裁剪项
        self.select_clip(clip)
        
        # 创建右键菜单
        menu = tk.Menu(self.window, tearoff=0)
        
        menu.add_command(
            label="📋 复制",
            command=lambda: self.copy_clip(clip.id)
        )
        
        menu.add_separator()
        
        menu.add_command(
            label="💾 导出...",
            command=self.export_selected
        )
        
        menu.add_command(
            label="✎ 重命名...",
            command=self.rename_selected
        )
        
        menu.add_command(
            label="📁 修改分类...",
            command=self.change_category
        )
        
        menu.add_separator()
        
        menu.add_command(
            label="🖼️ 替换文件...",
            command=lambda: self.replace_clip_file(clip)
        )
        
        menu.add_command(
            label="🪄 去除背景",
            command=lambda: self.remove_clip_background(clip)
        )
        
        menu.add_separator()
        
        menu.add_command(
            label="🗑️ 删除",
            command=self.delete_selected
        )
        
        menu.post(event.x_root, event.y_root)
    
    def switch_category(self, category: str):
        """
        切换分类
        
        Args:
            category: 分类名称
        """
        self.current_category = category
        self.selected_clip_id = None
        self.refresh()
        self.logger.debug(f"切换到分类: {category}")
    
    def rename_selected(self):
        """重命名选中的裁剪项"""
        if not self.selected_clip_id:
            messagebox.showwarning("警告", "请先选择要重命名的裁剪项！", parent=self.window)
            return
        
        clip = self.clip_manager.get_clip(self.selected_clip_id)
        if not clip:
            return
        
        # 创建重命名对话框
        dialog = tk.Toplevel(self.window)
        dialog.title("重命名裁剪项")
        dialog.geometry("400x150")
        dialog.transient(self.window)
        dialog.grab_set()
        
        # 居中显示
        dialog.update_idletasks()
        x = self.window.winfo_x() + (self.window.winfo_width() - dialog.winfo_width()) // 2
        y = self.window.winfo_y() + (self.window.winfo_height() - dialog.winfo_height()) // 2
        dialog.geometry(f"+{x}+{y}")
        
        # 内容
        ttk.Label(
            dialog,
            text=f"当前名称: {clip.name}",
            font=("Arial", 10)
        ).pack(pady=10)
        
        ttk.Label(dialog, text="新名称:").pack(pady=(10, 0))
        
        name_entry = ttk.Entry(dialog, width=40)
        name_entry.pack(pady=5, padx=20)
        name_entry.insert(0, clip.name)
        name_entry.select_range(0, tk.END)
        name_entry.focus()
        
        def do_rename():
            new_name = name_entry.get().strip()
            if not new_name:
                messagebox.showwarning("警告", "名称不能为空！", parent=dialog)
                return
            
            if self.clip_manager.rename_clip(self.selected_clip_id, new_name):
                dialog.destroy()
                self.refresh()
                messagebox.showinfo("成功", f"已重命名为: {new_name}", parent=self.window)
            else:
                messagebox.showerror("错误", "重命名失败！", parent=dialog)
        
        # 按钮
        btn_frame = ttk.Frame(dialog)
        btn_frame.pack(pady=10)
        
        ttk.Button(
            btn_frame,
            text="确定",
            command=do_rename,
            bootstyle="success",
            width=12
        ).pack(side=tk.LEFT, padx=5)
        
        ttk.Button(
            btn_frame,
            text="取消",
            command=dialog.destroy,
            bootstyle="secondary",
            width=12
        ).pack(side=tk.LEFT, padx=5)
        
        # 绑定回车键
        name_entry.bind("<Return>", lambda e: do_rename())
    
    def change_category(self):
        """修改选中裁剪项的分类"""
        if not self.selected_clip_id:
            messagebox.showwarning("警告", "请先选择要修改分类的裁剪项！", parent=self.window)
            return
        
        clip = self.clip_manager.get_clip(self.selected_clip_id)
        if not clip:
            return
        
        # 创建分类选择对话框
        dialog = tk.Toplevel(self.window)
        dialog.title("修改分类")
        dialog.geometry("350x250")
        dialog.transient(self.window)
        dialog.grab_set()
        
        # 居中显示
        dialog.update_idletasks()
        x = self.window.winfo_x() + (self.window.winfo_width() - dialog.winfo_width()) // 2
        y = self.window.winfo_y() + (self.window.winfo_height() - dialog.winfo_height()) // 2
        dialog.geometry(f"+{x}+{y}")
        
        # 内容
        ttk.Label(
            dialog,
            text=f"裁剪项: {clip.name}\n当前分类: {clip.category}",
            font=("Arial", 10)
        ).pack(pady=10)
        
        ttk.Label(dialog, text="选择新分类:").pack(pady=(10, 5))
        
        # 分类列表
        categories = self.clip_manager.get_categories()
        
        category_var = tk.StringVar(value=clip.category)
        
        for cat in categories:
            ttk.Radiobutton(
                dialog,
                text=cat,
                variable=category_var,
                value=cat
            ).pack(anchor=tk.W, padx=40)
        
        def do_change():
            new_category = category_var.get()
            if self.clip_manager.update_clip_category(self.selected_clip_id, new_category):
                dialog.destroy()
                self.refresh()
                messagebox.showinfo("成功", f"已修改分类为: {new_category}", parent=self.window)
            else:
                messagebox.showerror("错误", "修改分类失败！", parent=dialog)
        
        # 按钮
        btn_frame = ttk.Frame(dialog)
        btn_frame.pack(pady=15)
        
        ttk.Button(
            btn_frame,
            text="确定",
            command=do_change,
            bootstyle="success",
            width=12
        ).pack(side=tk.LEFT, padx=5)
        
        ttk.Button(
            btn_frame,
            text="取消",
            command=dialog.destroy,
            bootstyle="secondary",
            width=12
        ).pack(side=tk.LEFT, padx=5)
    
    def add_category(self):
        """添加新分类"""
        # 创建添加分类对话框
        dialog = tk.Toplevel(self.window)
        dialog.title("添加分类")
        dialog.geometry("400x150")
        dialog.transient(self.window)
        dialog.grab_set()
        
        # 居中显示
        dialog.update_idletasks()
        x = self.window.winfo_x() + (self.window.winfo_width() - dialog.winfo_width()) // 2
        y = self.window.winfo_y() + (self.window.winfo_height() - dialog.winfo_height()) // 2
        dialog.geometry(f"+{x}+{y}")
        
        # 内容
        ttk.Label(
            dialog,
            text="请输入新分类名称:",
            font=("Arial", 10)
        ).pack(pady=15)
        
        name_entry = ttk.Entry(dialog, width=40)
        name_entry.pack(pady=5, padx=20)
        name_entry.focus()
        
        def do_add():
            category_name = name_entry.get().strip()
            if not category_name:
                messagebox.showwarning("警告", "分类名称不能为空！", parent=dialog)
                return
            
            if self.clip_manager.add_category(category_name):
                dialog.destroy()
                self.refresh()
                messagebox.showinfo("成功", f"已添加分类: {category_name}")
            else:
                messagebox.showerror(
                    "错误",
                    f"添加分类失败！\n\n可能原因：\n1. 分类已存在\n2. 不能使用「全部」作为分类名",
                    parent=dialog
                )
        
        # 按钮
        btn_frame = ttk.Frame(dialog)
        btn_frame.pack(pady=15)
        
        ttk.Button(
            btn_frame,
            text="确定",
            command=do_add,
            bootstyle="success",
            width=12
        ).pack(side=tk.LEFT, padx=5)
        
        ttk.Button(
            btn_frame,
            text="取消",
            command=dialog.destroy,
            bootstyle="secondary",
            width=12
        ).pack(side=tk.LEFT, padx=5)
        
        # 绑定回车键
        name_entry.bind("<Return>", lambda e: do_add())
    
    def delete_category(self, category: str):
        """
        删除分类
        
        Args:
            category: 分类名称
        """
        # 确认删除
        clip_count = len(self.clip_manager.get_clips_by_category(category))
        
        if not messagebox.askyesno(
            "确认删除分类",
            f"确定要删除分类「{category}」吗？\n\n"
            f"该分类下有 {clip_count} 个裁剪项，\n"
            f"删除后这些裁剪项将移到「全部」分类。",
            parent=self.window
        ):
            return
        
        if self.clip_manager.remove_category(category):
            # 如果当前在被删除的分类，切换到"全部"
            if self.current_category == category:
                self.current_category = "全部"
            
            self.refresh()
            messagebox.showinfo("成功", f"分类「{category}」已删除", parent=self.window)
        else:
            messagebox.showerror("错误", "删除分类失败！", parent=self.window)
    
    def _on_canvas_configure(self, event):
        """画布配置改变"""
        # 调整内部框架宽度以匹配画布
        canvas_width = event.width
        self.canvas.itemconfig(self.canvas_window, width=canvas_width)

